﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

/**
 * \file MissionObject.cs
 */

/**
 * a type of CollidableObject that the Player
 * can bring back to the lab.
 */
public class MissionObject : DestructableObject
{
    /**
     * set to true if the interactable animation
     * for this object should be displayed.
     */
    protected Boolean nowUseable = false;
    protected Boolean highlighted = false;
    protected Boolean beingUsed = false;
    // Being successful implies that we were properly thrown onto a GoalTrigger
    protected bool isSuccessful = false;
    public bool isBeingUsed
    {
        get{return beingUsed;}
    }

    /**
     * determines whether this object can be 
     * destroyed or not. when false, this 
     * instance is indestructable. when true
     * this instance acts like a 
     * DestructableObject
     */
    protected Boolean activated = false;

    protected Animation arrow;
    protected Animation grabable;
    protected Animation supplement;

    /**
     * read/write. returns the world coordinates
     * for this object. when written to, this will
     * update the values for the collision box as well.
     */
    public new Vector3 position
    {
        get { return origin; }
        set 
        {
            Vector3 diff = value - origin;
            max += diff;
            min += diff;
            origin = value;
        }
    }


    public new float spriteDepth
    {
        get { return sDepth; }
        set { sDepth = value; }
    }

    /**
     * 
     */
    public MissionObject(MissionObject.Type type, Vector3 center)
        : base(type, center)
    {
        supplement = null;
        arrow = MissionObject.Type.getArrowAnimation();
        grabable = MissionObject.Type.getGrabableAnimation();
    }


    /**
     * tells the object to enter its grabable animation.
     * @param value true means this object displays
     * its grabable animation, false means this object
     * uses just its stasis animation
     */
    public virtual void setUseable(Boolean value)
    {
        nowUseable = value;
        if (!beingUsed)
        {
            if (nowUseable)
            {
                supplement = grabable;
            }
            else if (highlighted)
            {
                supplement = arrow;
            }
            else
            {
                supplement = null;
            }
        }
    }

    /**
     * tells the object to enter its highlighted animation.
     * @param value true means this object displays
     * its highlighted animation, false means this object
     * uses just its stasis animation
     */
    public void setHighlighting(Boolean value)
    {
        highlighted = value;
        if (!beingUsed)
        {
            if (highlighted)
            {
                supplement = arrow;
            }
            else if (nowUseable)
            {
                supplement = grabable;
            }
            else
            {
                supplement = null;
            }
        }
    }

    /**
     * Huzzah! We lived our purpose and were teleported away.
     */
    public void setSuccess()
    {
        isSuccessful = true;
    }

    public void use(Boolean value)
    {
        beingUsed = value;
        physics = !beingUsed;
        if (beingUsed)
        {
            sDepth = 0;
            activated = true;
            stayActive = true;
            supplement = null;
        }
        else
        {
            sDepth = thisType.spriteDepth;
            if (nowUseable)
            {
                supplement = grabable;
            }
            else if (highlighted)
            {
                supplement = arrow;
            }
            else
            {
                supplement = null;
            }
        }
    }

    public override void destroy(CollidableObject cobj)
    {
        if (activated)
        {
            base.destroy(cobj);
        }
    }

    /**
     * makes this object "poof" away and disappear.
     * the object will not respawn after this is called.
     */
    public virtual void disappear()
    {
        //create a poof animation
        cull = true;
    }

    public override void update(double elapsed)
    {
        base.update(elapsed);
        if (currVelocity.Length() == 0)
        {
            physics = false;
        }
        if (!isSuccessful && currentAnimation == destruction && currentAnimation.isFinished)
        {
            spawner.reset();
        }
        else if (supplement != null)
        {
            supplement.update(elapsed);
        }
    }

    public override void draw()
    {
        base.draw();
        if (supplement != null)
        {
            supplement.render(new Vector2(screenCoord.X, screenCoord.Y-thisType.collisionBoxDimentions.Y), screenCoord.Z + thisType.depthOffset/2);
        }
    }

    public Vector3 checkCollisions(Vector3 delta, CollidableObject ignore)
    {
        /*
        if (LevelBase.collidesWith(min + delta, max + delta, this).Count > 0)
        {
            return new Vector3(0, 0, 0);
        }
         */
        List<CollidableObject> collisions;// = LevelBase.collidesWith(min + delta, max + delta, this);
        if (delta.X > 0)
        {
            collisions = LevelBase.collidesWith(
                max - new Vector3(0, thisType.collisionBoxDimentions.Y - 1, thisType.collisionBoxDimentions.Z - 1),
                max + new Vector3(delta.X, -1, -1),
                this);
            foreach (CollidableObject c in collisions)
            {
                if (c != ignore && c.collisionMin.X < max.X + delta.X)
                {
                    delta.X = c.collisionMin.X - max.X;
                }
            }
        }
        else if (delta.X < 0)
        {
            collisions = LevelBase.collidesWith(
                min + new Vector3(delta.X, 1, 1),
                min + new Vector3(0, thisType.collisionBoxDimentions.Y - 1, thisType.collisionBoxDimentions.Z - 1),
                this);
            foreach (CollidableObject c in collisions)
            {
                if (c != ignore && c.collisionMax.X > min.X + delta.X)
                {
                    delta.X = c.collisionMax.X - min.X;
                }
            }
        }
        if (delta.Z > 0)
        {
            collisions = LevelBase.collidesWith(
                max - new Vector3(thisType.collisionBoxDimentions.X - 1, thisType.collisionBoxDimentions.Y - 1, 0),
                max + new Vector3(-1, -1, delta.Z),
                this);
            foreach (CollidableObject c in collisions)
            {
                if (c != ignore && c.collisionMin.Z < max.Z + delta.Z)
                {
                    delta.Z = c.collisionMin.Z - max.Z;
                }
            }
        }
        else if (delta.Z < 0)
        {
            collisions = LevelBase.collidesWith(
                min + new Vector3(1, 1, delta.Z),
                min + new Vector3(thisType.collisionBoxDimentions.X - 1, thisType.collisionBoxDimentions.Y - 1, 0),
                this);
            foreach (CollidableObject c in collisions)
            {
                if (c != ignore && c.collisionMax.Z > min.Z + delta.Z)
                {
                    delta.Z = c.collisionMax.Z - min.Z;
                }
            }
        }
        if (delta.Y > 0)
        {
            collisions = LevelBase.collidesWith(
                max - new Vector3(thisType.collisionBoxDimentions.X - 1, 0, thisType.collisionBoxDimentions.Z - 1),
                max + new Vector3(-1, delta.Y, -1),
                this);
            foreach (CollidableObject c in collisions)
            {
                if (c != ignore && c.collisionMin.Y < max.Y + delta.Y)
                {
                    delta.Y = c.collisionMin.Y - max.Y;
                }
            }
        }
        else if (delta.Y < 0)
        {
            collisions = LevelBase.collidesWith(
                min + new Vector3(1, delta.Y, 1),
                min + new Vector3(thisType.collisionBoxDimentions.X - 1, 0, thisType.collisionBoxDimentions.Z - 1),
                this);
            foreach (CollidableObject c in collisions)
            {
                if (c != ignore && c.collisionMax.Y > min.Y + delta.Y)
                {
                    delta.Y = c.collisionMax.Y - min.Y;
                }
            }
        }
        return delta;
    }

    public new class Type : DestructableObject.Type
    {

        //actual class data


        /**
         * this is a dummy constructor.
         */
        protected Type() { }

        /**
         * Private constructor.
         * This private constructor ensures that only 
         * the pre-specified Type objects can be used 
         * and compared against.
         * @param origin
         * @param collision
         * @param basic
         * @param destruction
         */
        protected Type(ObjectFactory.TypeEnum type, Vector3 origin, Vector3 collision, Boolean walkable,
            ArrayList stasisFrames, String stasisName, int stasisFramerate, Boolean stasisLoopable,
            ArrayList explosionFrames, String explosionName, int explosionFramerate, Boolean explosionLoopable)
            : base(type, origin, collision, walkable, stasisFrames, stasisName, stasisFramerate, stasisLoopable,
            explosionFrames, explosionName, explosionFramerate, explosionLoopable)
        {
        }

        protected Type(ObjectFactory.TypeEnum type, Vector3 origin, Vector3 collision,
            ArrayList stasisFrames, ArrayList explosionFrames)
            : base(type, origin, collision, stasisFrames, explosionFrames)
        {
        }

        public static Animation getArrowAnimation()
        {
            return new Animation(
                "arrow",//name
                12,//framerate
                true,//loopable
                new ArrayList { 
                    new Frame(
                        Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.ARROW).get("arrow"), 
                        new Rectangle(0, 0, 42, 37), 
                        0, 
                        new Vector2(21, 37)),
                    new Frame(
                        Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.ARROW).get("arrow"), 
                        new Rectangle(0, 0, 42, 37), 
                        0, 
                        new Vector2(21, 41)),
                    new Frame(
                        Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.ARROW).get("arrow"), 
                        new Rectangle(0, 0, 42, 37), 
                        0, 
                        new Vector2(21, 44)),
                    new Frame(
                        Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.ARROW).get("arrow"), 
                        new Rectangle(0, 0, 42, 37), 
                        0, 
                        new Vector2(21, 46)),
                    new Frame(
                        Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.ARROW).get("arrow"), 
                        new Rectangle(0, 0, 42, 37), 
                        0, 
                        new Vector2(21, 47)),
                    new Frame(
                        Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.ARROW).get("arrow"), 
                        new Rectangle(0, 0, 42, 37), 
                        0, 
                        new Vector2(21, 46)),
                    new Frame(
                        Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.ARROW).get("arrow"), 
                        new Rectangle(0, 0, 42, 37), 
                        0, 
                        new Vector2(21, 44)),
                    new Frame(
                        Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.ARROW).get("arrow"), 
                        new Rectangle(0, 0, 42, 37), 
                        0, 
                        new Vector2(21, 41))});//framelist
        }

        public static Animation getGrabableAnimation()
        {
            return new Animation(
                "grabable",//name
                12,//framerate
                true,//loopable
                new ArrayList { 
                    new Frame(
                        Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.USE).get("use"), 
                        new Rectangle(0, 0, 44, 44), 
                        0, 
                        new Vector2(22, 44)), 
                    new Frame(
                        Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.USE).get("use"), 
                        new Rectangle(0, 0, 44, 44), 
                        0, 
                        new Vector2(22, 48)), 
                    new Frame(
                        Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.USE).get("use"), 
                        new Rectangle(0, 0, 44, 44), 
                        0, 
                        new Vector2(22, 51)), 
                    new Frame(
                        Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.USE).get("use"), 
                        new Rectangle(0, 0, 44, 44), 
                        0, 
                        new Vector2(22, 53)), 
                    new Frame(
                        Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.USE).get("use"), 
                        new Rectangle(0, 0, 44, 44), 
                        0, 
                        new Vector2(22, 54)), 
                    new Frame(
                        Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.USE).get("use"), 
                        new Rectangle(0, 0, 44, 44), 
                        0, 
                        new Vector2(22, 53)), 
                    new Frame(
                        Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.USE).get("use"), 
                        new Rectangle(0, 0, 44, 44), 
                        0, 
                        new Vector2(22, 51)), 
                    new Frame(
                        Game.getGame().getAssetLibrary().get(AssetLibrary.assetType.USE).get("use"), 
                        new Rectangle(0, 0, 44, 44), 
                        0, 
                        new Vector2(22, 48)) });//framelist
        }
    }
}
